package models

import (
	"errors"
	"math"
	"math/rand"
	"sync"
	"time"
)

type ZUOBIAO struct {
	X int32
	Y int32
}

type S空间 struct {
	地图   sync.Map    //所有内容全在这里
	有效   bool        //是否还有存在的必要
	区域   *Struct区域定义 //在哪个区域中
	所有内容 sync.Map    //存放所有在此空间的内容
}

//注意
func (this *S空间) 判断坐标是否在当前空间(ue4x, ue4y int32) bool {
	x, y := F位置_UE4位置转游戏位置(ue4x, ue4y)
	if x < this.区域.X0 {
		return false
	}
	if x > this.区域.X1 {
		return false
	}
	if y < this.区域.Y0 {
		return false
	}
	if y > this.区域.Y1 {
		return false
	}
	return true
}
func 创建空间(副本名称 string) (*S空间, error) {
	ret := &S空间{}

	for _, v := range m_区域定义 {
		if v.V区域名称 == 副本名称 {
			ret.有效 = true
			ret.区域 = v
		}
	}
	if ret.有效 == false {
		return nil, errors.New("无效的空间名称")
	}

	for _, npc := range m_Npc列表 {
		ue4x, ue4y := F位置_游戏位置转UE4位置(npc.X, npc.Y)
		if ret.判断坐标是否在当前空间(ue4x, ue4y) == true {
			位置X, 位置Y := F位置_游戏位置转UE4位置(npc.X, npc.Y)
			ret.F添加内容(E类别_NPC, 位置X, 位置Y, npc.Z, npc.V方向, npc)
		}
	}

	//复制并召唤怪物
	for _, 怪物表 := range m_怪物生成表 {
		for X, Y := range 怪物表.V坐标 {
			ue4x, ue4y := F位置_游戏位置转UE4位置(X, Y)
			if ret.判断坐标是否在当前空间(ue4x, ue4y) == true {
				ret.召唤怪物(怪物表)
				break
			}
		}
	}
	//创建怪物...
	go ret.F清除过期物品()
	ret.有效 = true
	return ret, nil
}

//直接找到指定角色
func 地图_找到指定角色(名字 string) *StructMap {
	if t, ok := m_RunTime.Load(名字); ok == false {
		return nil
	} else {
		return t.(*RunTime).V角色内容
	}
}
func (this *S空间) F获取内容(内容ID int32) *StructMap {
	ret, ok := this.所有内容.Load(内容ID)
	if ok == true {
		return ret.(*StructMap)
	} else {
		return nil
	}
}

/*
//这个地方可以优化，可以定义9格，然后检查哪格可被选中，然后再进行搜索，这样可大大减少搜索频率
func (this *S空间) F找到四周内容指定ID(x, y int32, 距离_米 float32, 内容ID int32) *StructMap {
	var ret *StructMap
	x0 := x/方格大小 - 1
	y0 := y/方格大小 - 1
	var 方格 [9]int64

	for i := int32(0); i < 3; i++ {
		for ii := int32(0); ii < 3; ii++ {
			方格[i*3+ii] = (int64(x0+i) << 32) | int64(y0+ii)
		}
	}

	for _, v := range 方格 {
		if v == 0 { //如果不需要搜索的就不搜索了
			continue
		}
		items, ok := this.地图.Load(v)
		if ok == false {
			continue
		}
		item := items.(*sync.Map)
		item.Range(func(key, value interface{}) bool {
			v := value.(*StructMap)
			if v.Id != 内容ID {
				return true
			}
			if this.F计算两点距离(x, y, v.X, v.Y) > 距离_米 {
				return true
			}
			ret = v
			return false
		})
		if ret != nil {
			break
		}
	}
	return ret
}*/
//这个地方可以优化，可以定义9格，然后检查哪格可被选中，然后再进行搜索，这样可大大减少搜索频率
func (this *S空间) F找到指定位置四周内容(x, y int32, 距离_米 float32, 类型掩码 uint8) []*StructMap {
	var ret []*StructMap
	x0 := x/方格大小 - 1
	y0 := y/方格大小 - 1
	var 方格 [9]int64

	for i := int32(0); i < 3; i++ {
		for ii := int32(0); ii < 3; ii++ {
			方格[i*3+ii] = (int64(x0+i) << 32) | int64(y0+ii)
		}
	}

	for _, v := range 方格 {
		if v == 0 { //如果不需要搜索的就不搜索了
			continue
		}
		items, ok := this.地图.Load(v)
		if ok == false {
			continue
		}
		item := items.(*sync.Map)
		item.Range(func(key, value interface{}) bool {
			v := value.(*StructMap)
			if (uint8(v.V类别) & 类型掩码) == 0 {
				return true
			}
			if this.F计算两点距离(x, y, v.X, v.Y) > 距离_米 {
				return true
			}
			ret = append(ret, v)
			return true
		})
	}
	return ret
}

//功能 计算两点间距离 返回的单位为米
func (this *S空间) F计算两点距离(x0, y0, x1, y1 int32) float32 {
	距离 := math.Sqrt(float64((x0-x1)*(x0-x1)+(y0-y1)*(y0-y1))) / 100
	return float32(距离)
}

//功能 当角色移动位置时,使用此函数
func (this *StructMap) F内容移动(newX, newY int32) {
	oldzb := (int64(this.X/方格大小) << 32) | int64(this.Y/方格大小)
	zb := (int64(newX/方格大小) << 32) | int64(newY/方格大小)
	if oldzb != zb { //如果所在区不一样了，就需要重新分配了
		if t, ok := this.V空间.地图.Load(oldzb); ok == true {
			oldmap := t.(*sync.Map)
			oldmap.Delete(this.Id)
		} else {
			panic("怎么会找不到呢?")

		}
		var newmap sync.Map
		newmap.Store(this.Id, this)
		if t, ok := this.V空间.地图.LoadOrStore(zb, &newmap); ok == true { //两步合成一步，防止冲突
			t.(*sync.Map).Store(this.Id, this)
		}
	}
	this.X = newX
	this.Y = newY
}

//功能 向全局地图中添加内容
func (this *S空间) F添加内容(类别 E地图内容类别, x int32, y int32, z int32, 方向 float32, 内容 interface{}) *StructMap {
	add := &StructMap{}
	if nmap, ok := 内容.(*StructMap); ok == true {
		nmap.V空间.F删除内容(nmap.Id) //删除自已
		nmap.V空间 = this
		nmap.X = x
		nmap.Y = y
		add = nmap
	} else {
		add.Id = GetMapId() //获取一个全局ID
		add.V类别 = 类别
		switch 类别 {
		case E类别_NPC: //NPC设置在哪就在哪
		default:
			x += int32(rand.Intn(200) - 100) //掉落分散
			y += int32(rand.Intn(200) - 100)
		}
		add.X = x
		add.Y = y
		add.V方向 = 方向
		add.V内容 = 内容
		add.V空间 = this
		switch 类别 {
		case E类别_物品:
			add.V添加时间 = time.Now()
		case E类别_人物, E类别_怪物:
			add.VChan = make(chan *S交换数据, 30) //最少接收10个信息
			add.V基础属性 = make(map[uint16]float32)
			add.V额外属性 = make(map[uint16]S额外属性)
		}
	}
	zb := (int64(x/方格大小) << 32) | int64(y/方格大小)
	if t, ok := this.地图.Load(zb); ok == false {
		var newmap sync.Map
		newmap.Store(add.Id, add)
		this.地图.Store(zb, &newmap)
	} else {
		newmap := t.(*sync.Map)
		newmap.Store(add.Id, add)
	}
	this.所有内容.Store(add.Id, add)
	return add
}

//功能 向全局地图中删除内容
func (this *S空间) F删除内容(id int32) *StructMap {
	ret, ok := this.所有内容.Load(id)
	if ok == false {
		return nil
	}
	this.所有内容.Delete(id) //删除
	内容 := ret.(*StructMap)
	zb := (int64(内容.X/方格大小) << 32) | int64(内容.Y/方格大小)
	if t, ok := this.地图.Load(zb); ok == true {
		newmap := t.(*sync.Map)
		newmap.Delete(id)
	}
	return 内容
}

//功能 清除地图中过期的，没有人捡取的物品
func (this *S空间) F清除过期物品() {
	for {
		if this.有效 == false {
			return
		}
		time.Sleep(time.Second * 5) //每5秒刷新一次
		{

			this.地图.Range(func(_, m interface{}) bool {
				片 := m.(*sync.Map)
				片.Range(func(id, 内容 interface{}) bool {
					v := 内容.(*StructMap)
					if v.V类别 != E类别_物品 {
						return true
					}
					if (v.Id < 0) && (time.Now().Sub(v.V添加时间).Seconds() < float64(30)) { //临时物品30秒消失
						return true
					} else if (v.Id > 0) && (time.Now().Sub(v.V添加时间).Seconds() < float64(120)) { //角色掉落的物品120秒消失
						return true
					}
					片.Delete(v.Id)
					if v.Id > 0 { //这是角色扔掉的物品
						db.Where("id=?", v.Id).Delete(v.V内容)
					}
					return true
				})
				return true
			})
		}
	}
}

//功能 获取一个新的地图ID

var mapid int32
var mapid_lock sync.Mutex

func GetMapId() int32 {
	mapid_lock.Lock()
	defer mapid_lock.Unlock()
	if mapid < 0x00070000 {
		mapid = 0x00070000
	}
	mapid++
	return mapid
}
func F位置_UE4位置转游戏位置(x, y int32) (int32, int32) {
	return x/100 + 26009, y/100 + 26009
}
func F位置_游戏位置转UE4位置(x int32, y int32) (int32, int32) {
	return (x - 26009) * 100, (y - 26009) * 100
}
